﻿using Infrastructure.Serializer;
using InventoryManagementSystem.DataModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Xaml;
using WinRTDatabase;

namespace InventoryManagementSystem
{
    static class DataStore
    {
        private static Database database;

        private static HttpClient client = new HttpClient();

        private static DispatcherTimer syncTimer = new DispatcherTimer();

        private static Table<Product> productTable;

        private static Table<Product> productCacheTable;
        private static Table<Order> purchaseOrderCacheTable;
        private static Table<Order> sellOrderCacheTable;

        private static bool isInitialized;
        private static DateTime date;

        private const string CacheDatabaseName = "InventoryManagment";

        private const string ProductTableName = "Product";
        private const string PurchaseOrderTableName = "PurchaseOrder{0:yyyyMMdd}";
        private const string SellOrderTableName = "SellOrder{0:yyyyMMdd}";

        private const string ProductCacheTableName = "ProductCache";
        private const string PurchaseOrderCacheTableName = "PurchaseOrderCache";
        private const string SellOrderCacheTableName = "SellOrderCache";

        public async static Task<bool> Initialize()
        {
            if (isInitialized)
                return false;

            date = DateTime.Now;

            if (await Database.DoesDatabaseExistsAsync(CacheDatabaseName))
            {
                database = await Database.OpenDatabaseAsync(CacheDatabaseName, false, StorageLocation.Local);
            }
            else
            {
                database = await Database.CreateDatabaseAsync(CacheDatabaseName);
                
                database.CreateTable<Product>(ProductTableName);
                
                database.CreateTable<Product>(ProductCacheTableName);
                database.CreateTable<Order>(PurchaseOrderCacheTableName);
                database.CreateTable<Order>(SellOrderCacheTableName);
                
                await database.SaveAsync();
            }

            productTable = await database.Table<Product>(ProductTableName);

            productCacheTable = await database.Table<Product>(ProductCacheTableName);
            purchaseOrderCacheTable = await database.Table<Order>(PurchaseOrderCacheTableName);
            sellOrderCacheTable = await database.Table<Order>(SellOrderCacheTableName);

            isInitialized = true;

            return true;
        }

        public async static Task<Permission> LoginAsync(User user)
        {
            var userXml = XmlSerializer.Serialize(user);

            client.DefaultRequestHeaders.Add("User", Uri.EscapeDataString(userXml));

            var loginResponse = await client.GetAsync(AppConfig.LoginUri);

            loginResponse = loginResponse.EnsureSuccessStatusCode();
            var permission = loginResponse.Headers.GetValues("Permission").First();

            //syncTimer.Tick += syncTimer_Tick;
            //syncTimer.Interval = TimeSpan.FromHours(1);
            //syncTimer.Start();

            return (Permission)Enum.Parse(typeof(Permission), permission);
        }

        //private async static void syncTimer_Tick(object sender, object e)
        //{
        //    await MergeCacheAsync();
        //}

        public async static void AddProductAsync(Product product)
        {
            product = product.Clone();

            product.Id = Guid.NewGuid();

            productCacheTable.Add(product);
            await database.SaveAsync();
        }

        public static Product GetProduct(string barCode)
        {
            var product = productTable.Concat(productCacheTable).Where(p => p.BarCode == barCode).SingleOrDefault();

            if (product != null)
                product = product.Clone();

            return product;
        }

        public static IEnumerable<Product> GetProducts()
        {
            return productTable.Concat(productCacheTable);
        }

        public async static void AddPurchaseOrder(Order order)
        {
            var now = DateTime.Now;

            order = order.Clone();

            var existedProduct = DataStore.GetProduct(order.Product.BarCode);

            if (existedProduct == null)
            {
                AddProductAsync(order.Product);
                existedProduct = DataStore.GetProduct(order.Product.BarCode);
            }

            order.Id = Guid.NewGuid();
            order.Product = existedProduct;
            order.Time = now;

            purchaseOrderCacheTable.Add(order);
            await database.SaveAsync();
        }

        public async static void AddSellOrders(IEnumerable<Order> orders)
        {
            var now = DateTime.Now;

            foreach (var o in orders)
            {
                var order = o.Clone();

                order.Id = Guid.NewGuid();
                order.Time = now;

                sellOrderCacheTable.Add(order);
            }

            await database.SaveAsync();
        }

        public async static Task<IEnumerable<Order>> GetSellOrdersByDate(DateTime date)
        {
            var tableName = string.Format(SellOrderTableName, date);
            if (!database.DoesTableExists(typeof(Order), tableName))
                return sellOrderCacheTable.Where(order => order.Time.DayOfYear == date.DayOfYear);

            var sellOrderTable = await database.Table<Order>(tableName);
            return sellOrderTable.Concat(sellOrderCacheTable.Where(order => order.Time.DayOfYear == date.DayOfYear));
        }

        //public async static Task<bool> DownloadDataAsync()
        //{
        //    if (!await DownloadDataAsync(AppConfig.DownloadProductsUri, productTable))
        //        return false;

        //    if (!await DownloadDataAsync(AppConfig.DownloadPurchaseOrdersUri, purchaseTable))
        //        return false;

        //    if (!await DownloadDataAsync(AppConfig.DownloadProductsUri, sellTable))
        //        return false;

        //    return true;
        //}

        //public async static Task<bool> DownloadDataAsync<T>(string uri, Table<T> table)
        //{
        //    try
        //    {
        //        var response = await client.GetAsync(uri);
        //        response.EnsureSuccessStatusCode();

        //        var xml = await (response.Content as StringContent).ReadAsStringAsync();
        //        var items = XmlSerializer.Deserialize<List<T>>(xml);

        //        table.Clear();
        //        table.AddRange(items);
        //        await database.SaveAsync();

        //        return true;
        //    }
        //    catch
        //    {
        //        return false;
        //    }
        //}

        public async static Task<bool> MergeCacheAsync()
        {
            //try
            //{
            //    var productXml = XmlSerializer.Serialize(productCacheTable);
            //    var productResponse = await client.PostAsync(AppConfig.UploadProductsUri, new StringContent(productXml));

            //    productResponse = productResponse.EnsureSuccessStatusCode();

            //    if (productResponse.Headers.GetValues("Result").First() != "OK")
            //        return false;

            //    var purchaseOrderXml = XmlSerializer.Serialize(purchaseOrderCacheTable);
            //    var purchaseOrderResponse = await client.PostAsync(AppConfig.UploadPurchaseOrdersUri, new StringContent(purchaseOrderXml));

            //    purchaseOrderResponse = productResponse.EnsureSuccessStatusCode();

            //    if (purchaseOrderResponse.Headers.GetValues("Result").First() != "OK")
            //        return false;

            //    var sellOrderXml = XmlSerializer.Serialize(sellOrderCacheTable);
            //    var sellOrderResponse = await client.PostAsync(AppConfig.UploadSellOrdersUri, new StringContent(sellOrderXml));

            //    sellOrderResponse = productResponse.EnsureSuccessStatusCode();

            //    if (sellOrderResponse.Headers.GetValues("Result").First() != "OK")
            //        return false;
            //}
            //catch
            //{
            //    return false;
            //}

            foreach (var product in productCacheTable)
            {
                productTable.Add(product);
            }
            productCacheTable.Clear();

            Table<Order> purchaseOrderTable = null;
            int lastPurchaseOrderDayOfYear = -1;
            foreach (var purchaseOrder in purchaseOrderCacheTable)
            {
                if (purchaseOrderTable == null || lastPurchaseOrderDayOfYear != purchaseOrder.Time.DayOfYear)
                {
                    var tableName = string.Format(PurchaseOrderTableName, purchaseOrder.Time);
                    if (!database.DoesTableExists(typeof(Order), tableName))
                    {
                        database.CreateTable<Order>(tableName);
                    }
                    purchaseOrderTable = await database.Table<Order>(tableName);
                    lastPurchaseOrderDayOfYear = purchaseOrder.Time.DayOfYear;
                }

                purchaseOrderTable.Add(purchaseOrder);
                productTable.Where(p => p.Id == purchaseOrder.Product.Id).Single().Count += purchaseOrder.Count;
            }
            purchaseOrderCacheTable.Clear();

            Table<Order> sellOrderTable = null;
            int lastSellDayOfYear = 0;
            foreach (var sellOrder in sellOrderCacheTable)
            {
                if (sellOrderTable == null || lastSellDayOfYear != sellOrder.Time.DayOfYear)
                {
                    var tableName = string.Format(SellOrderTableName, sellOrder.Time);
                    if (!database.DoesTableExists(typeof(Order), tableName))
                    {
                        database.CreateTable<Order>(tableName);
                    }
                    sellOrderTable = await database.Table<Order>(tableName);
                    lastSellDayOfYear = sellOrder.Time.DayOfYear;
                }

                sellOrderTable.Add(sellOrder);
                productTable.Where(p => p.Id == sellOrder.Product.Id).Single().Count -= sellOrder.Count;
            }
            sellOrderCacheTable.Clear();

            await database.SaveAsync();

            return true;
        }
    }
}
